import copy
import turtle
import math
import random
import time

def DrawaR(x, y, w, h):  # 画一个方块
    # 填充色
    turtle.begin_fill()
    turtle.penup()
    turtle.goto(x, y)
    turtle.pendown()
    turtle.forward(w)
    turtle.right(90)
    turtle.forward(h)
    turtle.right(90)
    turtle.forward(w)
    turtle.right(90)
    turtle.forward(h)
    turtle.right(90)
    turtle.end_fill()

    # 线条色
    turtle.penup()
    turtle.goto(x, y)
    turtle.pendown()
    turtle.forward(w)
    turtle.right(90)
    turtle.forward(h)
    turtle.right(90)
    turtle.forward(w)
    turtle.right(90)
    turtle.forward(h)
    turtle.right(90)

# 迷宫生成与解密
class Maze:
    def __init__(self, x, y, w, h):
        self.X     = x  # 左上角x坐标
        self.Y     = y  # 左上角y坐标
        self.Width = w  # 像素：宽
        self.High  = h  # 像素：高
        self.map_w = 31  # 迷宫：宽
        self.map_h = self.map_w  # 迷宫：高
        self.maze_map = [[0 for _ in range(self.map_w)] for _ in range(self.map_h)]  # 储存迷宫的数据
        '''
        0 : 墙方格
        1 : 可探索方格
        2 : 探索过方格
        3 : 最终路径
        '''
        self.finding = False  # 记录是否在寻找
        self.generate()
    
    # 判断坐标是否在迷宫内部  
    def in_maze(self, x, y):  
        return 0 <= x < self.map_w and 0 <= y < self.map_h  
    
    # 生成迷宫的递归函数  
    def carve_passages(self, cx, cy):  
        directions = [(2,0), (-2,0), (0,2), (0,-2)]  
        random.shuffle(directions)  

        for dx, dy in directions:  
            nx, ny = cx + dx, cy + dy  
            if self.in_maze(nx, ny) and self.maze_map[ny][nx] == 0:  
                # 将中间墙打通  
                self.maze_map[cy + dy//2][cx + dx//2] = 1  
                self.maze_map[ny][nx] = 1  
                self.carve_passages(nx, ny) 
    
    def generate(self):  
        
        # 绘制背景
        self.ShowBackGround()
        self.maze_map = [[0 for _ in range(self.map_w)] for _ in range(self.map_h)]  # 储存迷宫的数据

        # 起点设为通路(1)  
        start_x, start_y = 0, 0  
        # 为了保证边界是墙，起点通常是奇数坐标，可以根据需要调整  
        if start_x % 2 == 0: start_x += 1  
        if start_y % 2 == 0: start_y += 1  
        
        self.maze_map[start_y][start_x] = 1  

        # 递归开路  
        self.carve_passages(start_x, start_y)
        self.maze_map[0][1] = 1
        self.maze_map[-1][-2] = 1

        self.draw()
    
    # 得到路径，在地图上
    def get_way(self):
        self.finding = False

        # 深度优先算法 dps
        self.RoadList = []  # 储存岔路口
        finded = [[0 for _ in range(self.map_w)] for _ in range(self.map_h)]
        self.direction    = [[0, 1], [1, 0], [-1, 0], [0, -1]]
        now_x = 0
        now_y = 1
        self.RoadList.append([now_x, now_y])
        finded[now_x][now_y] = 1
        maze_map = copy.deepcopy(self.maze_map)

        while now_x != self.map_w - 1 or now_y != self.map_h - 2:
            find_way = False
            for a, b, in self.direction:
                if self.in_maze(now_x + a, now_y + b) and maze_map[now_x + a][now_y + b] != 0 and finded[now_x + a][now_y + b] == 0:
                    find_way = True
                    now_x, now_y = now_x + a, now_y + b
                    finded[now_x][now_y] = 1
                    self.RoadList.append([now_x, now_y])
                
            if not find_way:
                now_x, now_y = self.RoadList[-1]
                            # 判断是的不是岔路
                count = 0
                # print(now_x , now_y)

                for a, b, in self.direction:
                    if self.in_maze(now_x + a, now_y + b) and maze_map[now_x + a][now_y + b] == 1 and finded[now_x + a][now_y + b] == 0:
                        # print(now_x + a, now_y + b, "-->1")
                        count += 1
                if count == 0:
                    self.RoadList.pop()
            
            # print(self.RoadList)

        return self.RoadList
    
    # 解密
    def decrypt(self):
        self.finding = True

        # 深度优先算法 dps
        self.RoadForkList = []  # 储存岔路口
        self.direction    = [[0, 1], [1, 0], [-1, 0], [0, -1]]
        now_x = 0
        now_y = 1
        maze_map = copy.deepcopy(self.maze_map)
        maze_map[now_x ][now_y] = 2
        self.draw_index(maze_map, now_x, now_y)

        while now_x != self.map_w - 1 or now_y != self.map_h - 2:
            
            # 判断是的不是岔路
            count = 0
            # print(now_x , now_y)

            for a, b, in self.direction:
                if self.in_maze(now_x + a, now_y + b) and (maze_map[now_x + a][now_y + b] == 1 or maze_map[now_x + a][now_y + b] == 3):
                    # print(now_x + a, now_y + b, "-->1")
                    count += 1
            if count >= 1:
                self.RoadForkList.append([now_x, now_y])

            find_way = False
            for a, b, in self.direction:
                if self.in_maze(now_x + a, now_y + b) and (maze_map[now_x + a][now_y + b] == 1 or maze_map[now_x + a][now_y + b] == 3):
                    find_way = True
                    now_x, now_y = now_x + a, now_y + b
                    maze_map[now_x][now_y] = 2
                    self.draw_index(maze_map, now_x, now_y)
                    turtle.update()  # 每帧绘制后调用更新强制刷新画面  
                    time.sleep(0.05)  # 适当延时，让绘制可见
                    break
                
            if not find_way:
                now_x, now_y = self.RoadForkList.pop()
            
            # 如果被提前终止了
            if self.finding == False:
                return
            # print(self.RoadForkList)
            # for l in maze_map:
            #     print(l)
            # print()

        self.finding = False

    # 绘制背景
    def ShowBackGround(self):
        turtle.color((0.6, 0.6, 0.6), ( 0.8, 0.8, 0.8))
        DrawaR(self.X, self.Y, self.Width, self.High)
    
    def draw_index(self, maze_map, i, j):
        if maze_map[i][j] in (0, 1, 2):
            if maze_map[i][j] == 0:
                turtle.color("black", "black")
            if maze_map[i][j] == 1:
                turtle.color("white", "white")
            if maze_map[i][j] == 2:
                turtle.color((1, 0.9, 0.6), (1, 0.9, 0.6))
                
            k = int(self.Width / self.map_w + 0.5)
            cell_x = self.X + i * k
            cell_y = self.Y - j * k  
            DrawaR(cell_x, cell_y, k, k)  
        else:
            turtle.color((0, 1, 0), (0, 1, 0))

            k = int(self.Width / self.map_w + 0.5)
            cell_x = self.X + i * k + k / 3
            cell_y = self.Y - j * k - k / 3
            DrawaR(cell_x, cell_y, k / 3, k / 3)  
            direction    = [[0, 1], [1, 0], [-1, 0], [0, -1]]
            # 连通路径
            for a, b, in direction:
                if self.in_maze(i + a, j + b) and maze_map[i + a][j + b] == 3:
                    DrawaR(cell_x + a * k / 3, cell_y - b * k / 3, k / 3, k / 3) 

    def draw(self):
        for i in range(self.map_h):
            for j in range(self.map_w):
                self.draw_index(self.maze_map, i, j)

class Button():
    def __init__(self, x, y, w, h):
        self.X     = x  # 左上角x坐标
        self.Y     = y  # 左上角y坐标
        self.Width = w  # 像素：宽
        self.High  = h  # 像素：高
        self.text   = ""
        self.cilck_fun = None

        self.mod  = 0
        self.last_mod = -1
        '''
        0: 默认颜色
        1: 点击时的颜色
        '''

        self.fill_color = ( 0, 0, 0)  # 按钮填充颜色-普通
        self.line_color = ( 1.0, 1.0, 0)  # 按钮轮廓颜色-普通

        self.click_fill_color = ( 0.2, 0.2, 0.2)  # 按钮填充颜色-被点击时
        self.click_line_color = ( 1.0, 1.0, 0)  # 按钮轮廓颜色-被点击时

        self.float_fill_color = ( 0.2, 0.2, 0.2)  # 按钮填充颜色-鼠标悬浮时
        self.float_line_color = ( 0, 1.0, 0)  # 按钮轮廓颜色-鼠标悬浮时
    
    def InMe(self, x, y):
        return self.X <=  x <= self.X + self.Width and self.Y - self.High <=  y <= self.Y

    def draw_text(self):
        # 写文字，x,y为按钮左上角，按钮中心：  
        center_x = self.X + self.Width / 2  
        center_y = self.Y - self.High / 2  

        turtle.penup()  
        turtle.goto(center_x, center_y - 10)  # 根据字体大小微调 y 坐标垂直居中  
        turtle.pendown()  

        turtle.color("white")  # 文字颜色，也可以自定义  
        turtle.write(self.text, align="center", font=("Arial", 16, "normal"))  

        turtle.penup()  # 写完文字后抬笔
    
    def change_mod(self, num):
        self.last_mod = self.mod
        self.mod = num
    
    def draw(self):
        if self.mod == self.last_mod:
            return 
        if self.mod == 0:
            turtle.color(self.line_color, self.fill_color)
            turtle.pensize(2)
            DrawaR(self.X, self.Y, self.Width, self.High)
            turtle.pensize(1)
        
        if self.mod == 1:
            turtle.color(self.click_line_color, self.click_fill_color)
            turtle.pensize(2)
            DrawaR(self.X, self.Y, self.Width, self.High)
            turtle.pensize(1)
        
        if self.mod == 2:
            turtle.color(self.float_line_color, self.float_fill_color)
            turtle.pensize(2)
            DrawaR(self.X, self.Y, self.Width, self.High)
            turtle.pensize(1)
        self.draw_text()
    

# 迷宫显示与用户交互
class Game:
    def __init__(self):
        turtle.tracer(False)  # 取消画笔动画
        turtle.hideturtle()  # 隐藏小海龟

        self.Width = 960
        self.High  = int(self.Width / 4 * 3)

        self.Button_list = []  # 储存内部的按钮
        self.generate_button = Button(self.High-self.Width/2 + 20, self.High / 2 - 20, self.Width-self.High - 60, 50)  # 生成迷宫按钮
        self.decrypt_button  = Button(self.High-self.Width/2 + 20 , self.High / 2 - 20 - 100, self.Width-self.High - 60, 50)  # 解密迷宫按钮
        self.get_ans_button  = Button(self.High-self.Width/2 + 20 , self.High / 2 - 20 - 200, self.Width-self.High - 60, 50)  # 直接得到答案按钮

        self.maze = Maze(-self.Width/2 + 20, self.High/2 - 20, self.High-40, self.High-40)
        
        self.SetUp()
    
    # 控件加载
    def SetUp(self):
        self.Button_list.append(self.generate_button)
        self.Button_list.append(self.decrypt_button)
        self.Button_list.append(self.get_ans_button)

        self.generate_button.cilck_fun = self.generate_maze
        self.decrypt_button.cilck_fun = self.decrypt_maze
        self.get_ans_button.cilck_fun = self.get_ans_maze

        self.generate_button.text = "生成迷宫"
        self.decrypt_button.text  = "解密迷宫"
        self.get_ans_button.text  = "查看答案"
    
    # 生成迷宫
    def generate_maze(self):
        print("generate_maze")
        self.maze.generate()

    # 逐步解密迷宫
    def decrypt_maze(self):
        print("decrypt_maze")
        self.maze.decrypt()
        self.get_ans_maze()

    # 得到答案：迷宫
    def get_ans_maze(self):
        print("get_ans_maze")
        ans = self.maze.get_way()
        maze_map = copy.deepcopy(self.maze.maze_map)
        for x, y in ans:
            maze_map[x][y] = 3
        for x, y in ans:
            self.maze.draw_index(maze_map, x, y)

    # 绘制背景
    def ShowBackGround(self):
        turtle.color(( 0.7, 0.7, 0.7), ( 0.7, 0.7, 0.7))
        DrawaR(-self.Width/2, self.High/2, self.Width, self.High)

    # 检测用户输入
    def GetInput(self, x, y):
        for button in self.Button_list:
            # 如果鼠标在按钮范围内：
            if button.InMe(x, y):
                button.change_mod(1)
                button.draw()
                button.cilck_fun()
            else:
                button.change_mod(0)
                button.draw()
            
    # 鼠标移动
    def MotionMove(self, event):
        def tk_to_turtle_coords(event, screen_w, screen_h):  
            return event.x - screen_w / 2, screen_h / 2 - event.y 
        canvas = turtle.getcanvas()  
        screen_width = canvas.winfo_width()  
        screen_height = canvas.winfo_height()  

        x, y = tk_to_turtle_coords(event, screen_width, screen_height)
        # print(f"Mouse moved to turtle coords: {x}, {y}")
        for button in self.Button_list:
            # 如果鼠标在按钮范围内：
            if button.InMe(x, y):
                button.change_mod(2)
                button.draw()
            else:
                button.change_mod(0)
                button.draw()

    # 运行
    def run(self):
        self.ShowBackGround()  # 背景绘制

        for button in self.Button_list:
            button.draw()

        self.maze.draw()
        canvas = turtle.getcanvas()               # 获取底层tkinter画布  
        canvas.bind('<Motion>', self.MotionMove)  # 绑定鼠标移动事件 
        turtle.onscreenclick(self.GetInput)       # 绑定鼠标点击事件
        turtle.mainloop()
    
if __name__ == "__main__":
    turtle.tracer(False)  # 取消画笔动画
    turtle.hideturtle()  # 隐藏小海龟

    game = Game()
    game.run()
